/**
 *  @file Socket.h
 */
#ifndef _SOCKET_H_
#define _SOCKET_H_

#include "../common/BaseObject.h"
#include "../common/CppMemPool.h"
#include "../text/CFString.h"
#include "../collections/ListT.h"
#include "../collections/ArrayT.h"
#include "../util/BitAttributeT.h"

namespace cppflib
{

namespace net
{

/**
 *  Interface to cancel a socket waiting operation
 *
 *  Steps to use this context:
 *
 *  1) Call Socket::SetAttribute(Socket::SA_CANCELWAIT, Boolean(true));
 *  2) Call Socket::GetCancelContext() and pass it to another thread
 *  3) Call one of the operations with timeout: Connect, ReceiveTimeout or ReceiveFromTimeout
 *  4) Call ICancelContext::CancelWaiting() to cancel the operation in another thread if necessary
 */
class _DLLAPI ICancelContext : public BaseObject
{
protected:
   ICancelContext() { }

public:
   virtual ~ICancelContext() { }

   /**
    *  Cancel an operation specified with a timeout value (e.g. Connect, ReceiveTimeout and 
    *  ReceiveFromTimeout) immediately. This function should be called in a thread different 
    *  from the thread performing the waiting operation.
    * 
    *  @note: This function *cannot* cancel blocking operations (e.g. Receive and ReceiveFrom)
    *
    *  @note: If this is to cancel a waiting "Connect", the cancelled socket must be closed 
    *         (Socket::Close) and initialized (Socket::Create) again.
    *
    *  @return true -- waiting is cancelled
    *  @return false -- operation disabled or socket is not in waiting state
    */
   virtual bool CancelWaiting() = 0;
};

class SocketCancelContext;

/**
 *  IPv4 socket class
 */
class _DLLAPI Socket : public BaseObject
{
public:
    /**
     *  sockattr_t
     */
    typedef enum _sockattr
    {
       SA_ISBLOCKING =         0x01,
       SA_ISCONNECTED =        0x02,
       SA_ISSERVER =           0x04,
       SA_CANCELWAIT =         0x08,   // Boolean (enable/disable cancel waiting)

       // for SSL socket
       SSL_CHK_PEERCERT =      0x100,
       SSL_CHK_VERIFYRESULT =  0x200,
       SSL_SENDRECV_WITHSSL =  0x400,  // Boolean
    } sockattr_t;

    PUBLIC const pcwstr_t LOCALHOST;  // local host IP

private:
    ptr_t sock; ///< socket handle
    SocketCancelContext *pCancelContext; ///< used to cancel a waiting operation
    
private:
    void Initialize(void);
    void SetHandle(ptr_t sock);
    PRIVATE int GetPendingSockets(collections::ICollection &sockList, sz_t listSize, ptrany_t fdSet, int timeout);

protected:
    util::BitAttributeT<sockattr_t> * sockAttr; ///< socket attribute
    ptr_t GetInternalSock();
    
public:
    //! socket type
    typedef enum {
       UnknownType = -1,
       Stream = 1,
       Dgram = 2,
       Raw = 3
    } SocketType;

    Socket(void);
    virtual ~Socket(void);

    virtual bool Create(const CFString &socketAddress, u16_t socketPort, SocketType socketType);
    bool Bind(u16_t socketPort, const CFString &socketAddress);
    bool IsValidSock();
    SocketType GetSocketType();
    bool GetPeerAddress(CFString &peerAddress, u16_t &peerPort);
    bool GetLocalAddress(CFString &localAddress, u16_t &localPort);
    virtual bool Accept(Socket &connectedSocket);
    virtual Socket * Accept();
    virtual void Close();
    virtual bool Connect(const CFString &hostAddress, u16_t hostPort);
    virtual bool Connect(const CFString &hostAddress, u16_t hostPort, int timeout);
    bool SetBlocking(bool isEnableBlock);
    bool IsBlocking();
    bool SetBroadcast(bool isEnableBroadcast);
    bool IsClosedByPeer(void);
    ICancelContext * GetCancelContext();
    virtual bool Listen(int connectionBacklog /* = 5 */);
    virtual int Receive(u8_t * pBuf, int bufLen, int nFlags);
    virtual int ReceiveTimeout(int timeout, u8_t * pBuf, int bufLen, int nFlags);
    virtual int ReceiveFrom(u8_t * pBuf, int bufLen, int nFlags, CFString &peerAddress, u16_t &peerPort);
    virtual int ReceiveFromTimeout(int timeout, u8_t * pBuf, int bufLen, int nFlags, CFString &peerAddress, u16_t &peerPort);
    virtual int Send(const u8_t * pBuf, int bufLen, int nFlags);
    virtual int SendTo(const u8_t * pBuf, int bufLen, int nFlags, const CFString &peerAddress, u16_t peerPort);
    virtual void SetAttribute(sockattr_t attr, const BaseObject &val);
    bool IsAttributeSet(sockattr_t attr);

    int ChkPendingData(int timeout);
    int ChkReadyToWrite(int timeout);

public:
    PUBLIC int ChkPendingSockets(collections::ListCppObjT<Socket> &sockList, int timeout);
    PUBLIC int ChkPendingSockets(collections::ArrayCppObjT<Socket> &sockArray, int timeout);

    friend class SocketCancelContext;
};

} // end of namespace net

} // end of namespace cppflib


#endif
